//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "aura.h"
#include "private.h"

#define _PAGE_SIZE          (1u << 12)
#define IS_PAGE_ALIGNED(va) (0 == ((va) & (_PAGE_SIZE -1)))

AURA_API AuraVirtualMemory_Map(
        /* [in] */ Aura_Address vBaseAddr,
        /* [in] */ Aura_MemorySize size,
        /* [in] */ Aura_MemoryMapFlags flags,
        /* [in] */ Aura_MemoryProtection protect,
        /* [out] */ Aura_Address *pBaseAddr)
{
    LPVOID pAddr = NULL;
    DWORD dwAllocateType, dwProtect;

    assert(IS_PAGE_ALIGNED(vBaseAddr));
    assert(size > 0);
    assert(IS_PAGE_ALIGNED(size));
    assert(NULL != pBaseAddr);

    dwAllocateType = ConvertToWinAllocateType(flags);
    dwProtect = ConvertToWinMemoryProection(protect);

    pAddr = VirtualAlloc((LPVOID)vBaseAddr,
                      (DWORD)size,
                      dwAllocateType,
                      dwProtect);
    if (NULL == pAddr) {
        goto E_FAIL_EXIT;
    }

    *pBaseAddr = (Aura_Address)pAddr;
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraVirtualMemory_Query(
        /* [in] */ Aura_Address vAddress,
        /* [out] */ Aura_Address *pBaseAddr,
        /* [out] */ Aura_MemorySize *pSize)
{
    MEMORY_BASIC_INFORMATION memoryInfo;
    DWORD dwRes = 0;

    assert(IS_PAGE_ALIGNED(vAddress));
    assert(NULL != pBaseAddr);
    assert(NULL != pSize);

    memset((void *)&memoryInfo, 0, sizeof(memoryInfo));
    dwRes = VirtualQuery((LPCVOID)vAddress,
                         &memoryInfo,
                         sizeof(memoryInfo));
    if (dwRes != sizeof(memoryInfo)) {
        goto E_FAIL_EXIT;
    }

    *pBaseAddr = (Aura_Address)memoryInfo.AllocationBase;
    *pSize     = memoryInfo.RegionSize;

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraVirtualMemory_SetProtection(
        /* [in] */ Aura_Address vBaseAddr,
        /* [in] */ Aura_MemorySize size,
        /* [in] */ Aura_MemoryProtection protect)
{
    DWORD dwNewProtect, dwOldProtect;
    MEMORY_BASIC_INFORMATION memoryInfo;
    DWORD dwRes = 0;
    BOOL bSuccess = FALSE;

    assert(IS_PAGE_ALIGNED(vBaseAddr));
    assert(size > 0);
    assert(IS_PAGE_ALIGNED(size));

    memset((void *)&memoryInfo, 0 , sizeof(memoryInfo));
    dwRes = VirtualQuery((LPCVOID)vBaseAddr,
                          &memoryInfo,
                          sizeof(memoryInfo));
    if (dwRes != sizeof(memoryInfo)) {
        goto E_FAIL_EXIT;
    }

    if ((memoryInfo.State == MEM_FREE)
        || (vBaseAddr != (Aura_Address)memoryInfo.AllocationBase)
        || (size   != (Aura_MemorySize)memoryInfo.RegionSize)) {
        return AURA_E_INVALID_ARGUMENT;
    }

    dwNewProtect = ConvertToWinMemoryProection(protect);
    if (dwNewProtect == memoryInfo.AllocationProtect) {
        return AURA_NOERROR;
    }

    bSuccess = VirtualProtect((LPVOID)vBaseAddr,
                              (DWORD)size,
                              dwNewProtect,
                              &dwOldProtect);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraVirtualMemory_CheckProtection(
        /* [in] */ Aura_Address vAddress,
        /* [in] */ Aura_MemoryProtection protect,
        /* [out] */ Aura_Bool *pIsValid)
{
    MEMORY_BASIC_INFORMATION memoryInfo;
    DWORD dwProtect = 0;
    DWORD dwRes = 0;

    assert(IS_PAGE_ALIGNED(vAddress));
    assert(NULL != pIsValid);

    memset((void *)&memoryInfo, 0, sizeof(memoryInfo));
    dwRes = VirtualQuery((LPCVOID)vAddress,
                         &memoryInfo,
                         sizeof(memoryInfo));
    if (dwRes != sizeof(memoryInfo)) {
        goto E_FAIL_EXIT;
    }

    dwProtect = ConvertToWinMemoryProection(protect);
    if (dwProtect == memoryInfo.Protect) {
        *pIsValid = TRUE;
    }
    else {
        *pIsValid = FALSE;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraVirtualMemory_Commit(
        /* [in] */ Aura_Address vAddress)
{
    MEMORY_BASIC_INFORMATION memoryInfo;
    DWORD dwRes = 0;
    LPVOID pAddr = NULL;

    assert(IS_PAGE_ALIGNED(vAddress));

    memset((void *)&memoryInfo, 0, sizeof(memoryInfo));
    dwRes = VirtualQuery((LPCVOID)vAddress,
                         &memoryInfo,
                         sizeof(memoryInfo));
    if (dwRes != sizeof(memoryInfo)) {
        goto E_FAIL_EXIT;
    }

    switch(memoryInfo.State) {
    case MEM_FREE:
        return AURA_E_DOES_NOT_EXIST;
    case MEM_COMMIT:
        return AURA_NOERROR;
    default:
        break;
    }
    pAddr = VirtualAlloc(memoryInfo.AllocationBase,
                    memoryInfo.RegionSize,
                    MEM_COMMIT,
                    memoryInfo.AllocationProtect);
    if (NULL == pAddr) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraVirtualMemory_IORemap(
        /* [in]  */ Aura_Address virtualAddr,
        /* [in]  */ Aura_MemorySize size,
        /* [in]  */ Aura_Address physicalAddr,
        /* [in]  */ Aura_MemoryMapFlags flags,
        /* [in]  */ Aura_MemoryProtection protect,
        /* [out] */ Aura_Address *pVirtualAddr)
{
    FixMe("Not Supported");
    return AURA_E_NOT_SUPPORTED;
}

AURA_API AuraVirtualMemory_Unmap(
        /* [in]  */ Aura_Address vBaseAddr,
        /* [in]  */ Aura_MemorySize size)
{
    MEMORY_BASIC_INFORMATION memoryInfo;
    BOOL bSuccess = FALSE;
    DWORD dwRes = 0;

    assert(IS_PAGE_ALIGNED(vBaseAddr));
    assert(size > 0);
    assert(IS_PAGE_ALIGNED(size));

    memset((void *)&memoryInfo, 0, sizeof(memoryInfo));
    dwRes = VirtualQuery((LPCVOID)vBaseAddr,
                        &memoryInfo,
                        sizeof(memoryInfo));
    if (dwRes != sizeof(memoryInfo)) {
        goto E_FAIL_EXIT;
    }
    if ((vBaseAddr != (Aura_Address)memoryInfo.AllocationBase)
        || (size != (Aura_MemorySize)memoryInfo.RegionSize)) {
        return AURA_E_INVALID_ARGUMENT;
    }

    bSuccess = VirtualFree((PVOID)vBaseAddr, 0, MEM_RELEASE);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

